/**@file
 * This header file is part of the I/O library; it contains the I/O context and
 * service declarations.
 *
 * An I/O context provides a mechanism to synchronize I/O services during
 * process forking and shutdown.
 *
 * @copyright 2018-2019 Lely Industries N.V.
 *
 * @author J. S. Seldenthuis <jseldenthuis@lely.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LELY_IO2_CTX_H_
#define LELY_IO2_CTX_H_

#include <lely/io2/io2.h>
#include <lely/util/dllist.h>

/**
 * The type of event generated by an I/O context before and after a process
 * fork.
 *
 * @see io_ctx_notify_fork()
 */
enum io_fork_event {
	/// The event generated before the fork.
	IO_FORK_PREPARE,
	/// The event generated after the fork in the parent process.
	IO_FORK_PARENT,
	/// The event generated after the fork in the child process.
	IO_FORK_CHILD
};

struct io_svc_vtbl;

/// An I/O service.
struct io_svc {
	/// A pointer to the virtual table for the I/O service.
	const struct io_svc_vtbl *vptr;
	int _shutdown;
	struct dlnode _node;
};

/// The static initializer for #io_svc.
#define IO_SVC_INIT(vptr) \
	{ \
		(vptr), 0, { NULL, NULL } \
	}

#ifdef __cplusplus
extern "C" {
#endif

/// The virtual table of an I/O service.
struct io_svc_vtbl {
	/**
	 * A pointer to the function to be called by io_ctx_notify_fork() (can
	 * be NULL).
	 *
	 * @returns 0 on success, or -1 on error.
	 */
	int (*notify_fork)(struct io_svc *svc, enum io_fork_event e);
	/**
	 * A pointer to the function to be called by io_ctx_shutdown() (can be
	 * NULL).
	 */
	void (*shutdown)(struct io_svc *svc);
};

void *io_ctx_alloc(void);
void io_ctx_free(void *ptr);
io_ctx_t *io_ctx_init(io_ctx_t *ctx);
void io_ctx_fini(io_ctx_t *ctx);

/**
 * Creates a new I/O context.
 *
 * @returns a pointer to the new context, or NULL on error. In the latter case,
 * the error number can be obtained with get_errc().
 */
io_ctx_t *io_ctx_create(void);

/// Destroys an I/O context. @see io_ctx_create()
void io_ctx_destroy(io_ctx_t *ctx);

/**
 * Registers an I/O service with an I/O context.
 *
 * @pre <b>svc</b> is not registered with any I/O context, including <b>ctx</b>.
 *
 * @see io_ctx_remove()
 */
void io_ctx_insert(io_ctx_t *ctx, struct io_svc *svc);

/**
 * Unregisters an I/O service with an I/O context.
 *
 * This function MUST NOT be invoked while io_ctx_notify_fork() or
 * io_ctx_shutdown() is running.
 *
 * @pre <b>svc</b> is registered with <b>ctx</b>.
 *
 * @see io_ctx_insert()
 */
void io_ctx_remove(io_ctx_t *ctx, struct io_svc *svc);

/**
 * Notifies all registered I/O services of the specified fork event. Services
 * are notified in order of registration, unless <b>e</b> is #IO_FORK_PREPARE,
 * in which case they are notified in reverse order.
 *
 * It is the responsibility of the caller to ensure that no services are
 * unregistered while this function is running.
 *
 * @returns 0 on success or -1 if any service returned an error. In the latter
 * case, the error number generated by the first failing service can be obtained
 * with get_errc().
 */
int io_ctx_notify_fork(io_ctx_t *ctx, enum io_fork_event e);

/**
 * Shuts down all registered I/O services in reverse order of registration. Each
 * service is shut down only once, irrespective of the number of calls to this
 * function.
 *
 * It is the responsibility of the caller to ensure that no services are
 * unregistered while this function is running.
 */
void io_ctx_shutdown(io_ctx_t *ctx);

#ifdef __cplusplus
}
#endif

#endif // !LELY_IO2_CTX_H_
